﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.Unity;

namespace Ewk.Configuration
{
    /// <summary>
    /// Resolves registered and known types.
    /// </summary>
    public class UnityDependencyResolver : IDependencyResolver
    {
        private readonly IUnityContainer _unityContainer;

        #region Constructors

        /// <summary>
        /// Initializes a new instance.
        /// </summary>
        public UnityDependencyResolver()
            : this(null)
        {
        }

        /// <summary>
        /// Initializes a new instance.
        /// </summary>
        /// <param name="unityContainer">The <see cref="IUnityContainer"/> that contains registered types.</param>
        public UnityDependencyResolver(IUnityContainer unityContainer)
        {
            _unityContainer = unityContainer ?? new UnityContainer();
        }

        #endregion

        #region Implementation of IDependencyResolver

        public object Resolve(Type type, params DependencyResolverParameterOverride[] parameterOverrides)
        {
            var overrides = ToParameterOverrideArray(parameterOverrides);

            return _unityContainer.Resolve(type, overrides);
        }

        public object Resolve(Type type, string name, params DependencyResolverParameterOverride[] parameterOverrides)
        {
            var overrides = ToParameterOverrideArray(parameterOverrides);

            return _unityContainer.Resolve(type, name, overrides);
        }

        public IEnumerable<object> ResolveAll(Type type)
        {
            return _unityContainer.ResolveAll(type);
        }

        public TReturn Resolve<TReturn>(params DependencyResolverParameterOverride[] parameterOverrides) where TReturn : class
        {
            return Resolve(typeof(TReturn), parameterOverrides) as TReturn;
        }

        public TReturn Resolve<TReturn>(string name, params DependencyResolverParameterOverride[] parameterOverrides) where TReturn : class
        {
            return Resolve(typeof(TReturn), name, parameterOverrides) as TReturn;
        }

        public IEnumerable<TReturn> ResolveAll<TReturn>() where TReturn : class
        {
            return ResolveAll(typeof (TReturn))
                .Cast<TReturn>();
        }

        public void Teardown(object o)
        {
            _unityContainer.Teardown(o);
        }

        public void EnsureIsRegistered<TFrom, TDefaultTo>()
            where TDefaultTo : TFrom
        {
            if (_unityContainer.IsRegistered<TFrom>())
            {
                return;
            }

            _unityContainer.RegisterType<TFrom, TDefaultTo>();
        }

        #endregion

        #region Implementation of IDisposable

        private bool _disposed;

        /// <summary>
        /// Disposes an instance of this class.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Dispose(bool disposing) executes in two distinct scenarios.
        /// If disposing equals true, the method has been called directly
        /// or indirectly by a user's code. Managed and unmanaged resources
        /// can be disposed.
        /// If disposing equals false, the method has been called by the
        /// runtime from inside the finalizer and you should not reference
        /// other objects. Only unmanaged resources can be disposed.
        /// </summary>
        /// <param name="disposing">Indicates if the method has been called by usercode (true) or by the runtime (false).</param>
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;
            if (!disposing) return;

            if (_unityContainer == null) return;
            _unityContainer.Dispose();

            _disposed = true;
        }

        /// <summary>
        /// Destructs an instance.
        /// </summary>
        ~UnityDependencyResolver()
        {
            Dispose(false);
        }

        #endregion

        private static ParameterOverride[] ToParameterOverrideArray(params DependencyResolverParameterOverride[] parameterOverrides)
        {
            var overrides = parameterOverrides
                .Select(p =>
                        new ParameterOverride(p.ParameterName, p.Instance));

            return overrides.ToArray();
        }
    }
}